/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.adaptor.method.readproperty;

//import Inspur.Gsp.Caf.Generator.BaseInfoItem.*;
//import Inspur.Gsp.Caf.Generator.ClassMethodGenerator.*;
//import Inspur.Gsp.Caf.Generator.Utils.*;
//import Inspur.Gsp.Cef.Api.Attribute.*;
//import Inspur.Gsp.Cef.Api.Repository.*;
//import Inspur.Gsp.Cef.Repository.*;
//import Inspur.Gsp.Cef.RTGenerator.Common.*;
//import Microsoft.CodeAnalysis.*;
//import Microsoft.CodeAnalysis.CSharp.*;
//import Microsoft.CodeAnalysis.CSharp.Syntax.*;

import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.cef.api.attr.AssoUdtAtrribute;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.api.repository.readerWriter.ICefReader;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.gencommon.ReflectionUtils;
import com.inspur.edp.cef.generator.overall.CefGeneratorContext;
import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import com.inspur.edp.cef.generator.repository.RepositoryGenUtil;
import com.inspur.edp.cef.generator.repository.adaptor.method.createpropertydefmethod.*;
import com.inspur.edp.cef.generator.repository.dac.DataTypeDacGenContext;
import com.inspur.edp.cef.repository.assembler.AssociationInfo;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfo;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfoCollection;
import com.inspur.edp.cef.repository.readerwriter.CefMappingReader;
import com.inspur.edp.cef.repository.repo.BaseRootRepository;
import com.inspur.edp.cef.spi.entity.IAuthFieldValue;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;

//
//     public abstract object readproperty(string propertyName, ICefReader reader)
//     {
//     switch(propertyName)
//     {
//       case "Code":
//       return reader.getString("Code");
//       case "DeptID":
//       return getTreeInfoAssValue(propertyName,reader);
//       Dictionary<string, string> mapping =new Dictionary<string, string>();
//       mapping.Add("Code1","Code");
//       mapping.Add("Name1","Name");
//       mapping.Add("TreeInfo1","TreeInfo");
//       CefMappingReader reader =new CefMappingReader(mapping);
//       treeInfoRepository.
//       default:
//       throw new Exception();
//     }
//     }
//
public class DataTypeReadPropertyGenerator extends ClassMethodGenerator {
	public DataTypeReadPropertyGenerator(DataTypeDacGenContext dacContext, IGspCommonDataType gspCommonDataType, String nodeCode, boolean isChild, DbColumnInfoCollection dbColumns) {
		this.dacContext = dacContext;
		this.gspCommonDataType = gspCommonDataType;
		this.nodeCode = nodeCode;
		this.isChild = isChild;
//		this.mappingPropNames = mappingPropNames;
//		this.dbColumns = dbColumns;
//		this.associationInfo = associationInfo;
	}

	public static final String paramPropName = "propertyName";
	public static final String paramReader = "reader";
	private DataTypeDacGenContext dacContext;
	private String nodeCode;
	private boolean isChild;
//	private java.util.HashMap<String, String> mappingPropNames;
//	private DbColumnInfoCollection dbColumns;
//	private java.util.ArrayList<AssociationInfo> associationInfo;
	private IGspCommonDataType gspCommonDataType;

	@Override
	protected Block buildMethodBody() {
		ast.newSwitchStatement();
		ast.newSwitchCase();
		SwitchStatement switchStatement = ast.newSwitchStatement();
		switchStatement.setExpression(ast.newSimpleName(paramPropName));
		java.util.ArrayList<Statement> statements = new java.util.ArrayList<Statement>();
		buildCaseStatements(statements);
		buildDefaultStatement(statements);
		switchStatement.statements().addAll(statements);

		Block block = ast.newBlock();
		block.statements().add(switchStatement);
		return block;
	}

	private void buildCaseStatements(java.util.ArrayList<Statement> statements) {
		for(IGspCommonField gspCommonField: this.gspCommonDataType.getContainElements()){
			buildCaseStatement(gspCommonField, statements);
		}
	}

	private void buildCaseStatement(IGspCommonField gspCommonField, java.util.ArrayList<Statement> statements) {
		java.util.ArrayList<Statement> appendedSections = new java.util.ArrayList<Statement>();
		java.util.ArrayList<Statement> caseStatments = new java.util.ArrayList<Statement>();
		if(gspCommonField.getIsVirtual() || gspCommonField.getObjectType() == GspElementObjectType.DynamicProp)
			return;
		if(gspCommonDataType instanceof ComplexDataTypeDef){
			if(((ComplexDataTypeDef)gspCommonDataType).getDbInfo().getMappingType() == ColumnMapType.SingleColumn) {
				return;
			}
		}
		if(gspCommonField.getIsUdt()){
			buildUdtStatement(gspCommonField, caseStatments, appendedSections);
		}
		else  {
			buildEntityStatement(gspCommonField, caseStatments, appendedSections);
		}


		String propertyName = gspCommonField.getLabelID();

		//todo wangmj
//		if (dbColumns.contains(propertyName)) {
			statements.add(getCaseLabel(propertyName));
			statements.addAll(caseStatments);
//		}
		statements.addAll(appendedSections);
	}

	private SwitchCase getCaseLabel(String propertyName) {
		SwitchCase switchCase = ast.newSwitchCase();
		switchCase.setExpression(RepositoryGenUtil.createStringLiteral(ast, propertyName));
		return switchCase;
	}


	private void buildUdtStatement(IGspCommonField gspCommonField, java.util.ArrayList<Statement> statements, java.util.ArrayList<Statement> appendedSections) {
		statements.add(createPropertyReturnStatement(CreateUdtDefMethodGenerator.getUdtPropertyMethodName(gspCommonField.getLabelID())));

		//关联udt增加带出字段的场景,N版info两个属性名是一致的可以用来判断,J版不一致增加enrichedParentAssoProperty单独判断
		if(gspCommonField.getChildAssociations() != null && gspCommonField.getChildAssociations().size() >0){
			GspAssociation gspAssociation = gspCommonField.getChildAssociations().get(0);
			for(IGspCommonField field:gspAssociation.getRefElementCollection()){
				boolean appendPropertyNameSufix = false;
				if(field.getIsFromAssoUdt())
				{
					appendPropertyNameSufix = false;
				}
				buildAssocationRefColumnStatement(gspCommonField.getChildAssociations().get(0), field, gspCommonField.getLabelID(), appendedSections, appendPropertyNameSufix);
			}
		}
	}

	private boolean isAssoUdt(java.lang.Class udtType) {
		//foreach (var propertyInfo in udtType.getProperties())
		//{
		//    var assoInterface = propertyInfo.PropertyType.getInterface("IAuthFieldValue");
		//    if (assoInterface != null)
		//        return true;
		//}

		//return false;
		//TODO 先不支持多值UDT中的关联
		boolean isAsso = false;
		boolean containAssoAttr = false;
		for (Method propertyInfo : udtType.getMethods()) {
			Class propType = propertyInfo.getReturnType();
//TODO:GetInterface
//			var assoInterface = propType.getInterface("IAuthFieldValue");
			boolean hasIAuthFieldValue = propType.isInstance(RepositoryGenUtil.getClassByClassName(IAuthFieldValue.class.getTypeName()).getTypeName());
			if (hasIAuthFieldValue) {
				isAsso = true;
			}
			AssoUdtAtrribute assoUdtAttributes = (AssoUdtAtrribute) ReflectionUtils.getMethodAnnotation(propertyInfo, AssoUdtAtrribute.class);
			if (assoUdtAttributes != null) {
				containAssoAttr = true;
			}
		}

		if (isAsso && containAssoAttr) {
			return true;
		}
		return false;
	}

	private void buildNormalPropertyReturnStatement(IGspCommonField gspCommonField, java.util.ArrayList<Statement> statements) {
		statements.add(createPropertyReturnStatement(NormalPropertyCreateDefMethodGenerator.getMethodName(gspCommonField.getLabelID())));
	}


	private void buildEnumStatement(IGspCommonField gspCommonField, java.util.ArrayList<Statement> statements) {
		statements.add(createPropertyReturnStatement(EntityCreateEnumDefMethodGenerator.getEnumPropertyValueMethodName(gspCommonField.getLabelID())));
	}

	private void buildAssociationStatement(IGspCommonField gspCommonField, String propertyName, java.util.ArrayList<Statement> statements, java.util.ArrayList<Statement> appendedSections) {
		statements.add(createPropertyReturnStatement(EntityCreateAssocationDefMethodGenerator.getAssPropertyValueMethodName(propertyName)));

		buildAssocationRefColumnStatements(gspCommonField.getChildAssociations().get(0), gspCommonField, propertyName, statements, appendedSections);
	}

	/**
	 * return this.methodName(reader);
	 *
	 * @param methodName
	 * @return
	 */
	private ReturnStatement createPropertyReturnStatement(String methodName) {
		ReturnStatement returnStatement = ast.newReturnStatement();
		ArrayList arguments = new ArrayList();
		arguments.add(ast.newSimpleName(paramReader));
		MethodInvocation methodInvocation = RepositoryGenUtil.createMethodInvocation(ast, ast.newThisExpression(), methodName, arguments);
		returnStatement.setExpression(methodInvocation);
		return returnStatement;
	}

//	private AssociationInfo getAssociationInfo(String propertyName) {
//		for (AssociationInfo item : associationInfo) {
//			if (propertyName.equals(item.getSourceColumn())) {
//				return item;
//			}
//		}
//		throw new RuntimeException("没有找到关联信息" + propertyName);
//	}

	private void buildAssocationRefColumnStatements(GspAssociation associationInfo, IGspCommonField gspCommonField, String propertyName, java.util.ArrayList<Statement> statements, java.util.ArrayList<Statement> appendedSections) {
		if(gspCommonField.getChildAssociations() !=null && gspCommonField.getChildAssociations().size() > 0){
			GspAssociation gspAssociation = gspCommonField.getChildAssociations().get(0);
			for(IGspCommonField field: gspAssociation.getRefElementCollection()){
				if(propertyName.equals(field.getLabelID())){
					continue;
				}
				buildAssocationRefColumnStatement(associationInfo, field, propertyName, appendedSections, false);
			}
		}
	}


	private void buildAssocationRefColumnStatement(GspAssociation associationInfo, IGspCommonField gspCommonField, String propertyName, java.util.ArrayList<Statement> appendedSections, boolean appendPropertyNameSufix) {

		String readedPropertyName = "";
		RefAssociationInfo refAssociationInfo = getRefAssociation(dacContext.getReposContext().getCefContext().getCefGenContext(), gspCommonField, associationInfo);
		readedPropertyName = refAssociationInfo.getRefField().getLabelID();
//		if(readedPropertyName == null || readedPropertyName.isEmpty()){
//			return;
//		if (appendPropertyNameSufix) {
//			readedPropertyName = EntityCreateAssocationDefMethodGenerator.getRefColumn(dacContext.getReposContext().getCefContext().getCefGenContext(), propertyName + "_" + gspCommonField.getLabelID(), associationInfo, gspCommonField, gspCommonDataType);
//			if(readedPropertyName == null || readedPropertyName.isEmpty()){
//				return;
////				throw new RuntimeException(propertyName + "_" + refAssInfo.getPropertyName() + "未获取到关联信息,请点击【同步业务字段】");
//			}
//		} else {
//			readedPropertyName = EntityCreateAssocationDefMethodGenerator.getRefColumn(dacContext.getReposContext().getCefContext().getCefGenContext(), gspCommonField.getLabelID(), associationInfo, gspCommonField, gspCommonDataType);
//			if(readedPropertyName == null || readedPropertyName.isEmpty()){
////				throw new RuntimeException(refAssInfo.getPropertyName() + "未获取到关联信息,请点击【同步业务字段】");
//				return;
//			}
//		}

		String caseLabelName = gspCommonField.getLabelID();
		if (appendPropertyNameSufix) {
			caseLabelName = propertyName + "_" + gspCommonField.getLabelID();
		}
		java.util.ArrayList<Statement> appendStatements = new java.util.ArrayList<Statement>();
		String mappingName = String.format("%1$s%2$sMapping", propertyName, gspCommonField.getLabelID());
		String readerName = String.format("%1$s%2$sReader", propertyName, gspCommonField.getLabelID());
		String adapterName = String.format("%1$s%2$sAdapter", propertyName, gspCommonField.getLabelID());
		addImport(CefMappingReader.class.getTypeName());
		addImport(BaseRootRepository.class.getTypeName());
		EntityCreateAssocationDefMethodGenerator.addInitMappingStatement(ast, appendStatements, propertyName, mappingName);
		EntityCreateAssocationDefMethodGenerator.addNewMappingReaderStatement(ast, appendStatements, readerName, mappingName);
		EntityCreateAssocationDefMethodGenerator.addNewAdapterStatement(ast, appendStatements, propertyName, adapterName, associationInfo.getRefObjectCode());

		ArrayList arguments = new ArrayList();
		StringLiteral arg1 = RepositoryGenUtil.createStringLiteral(ast, readedPropertyName);
		Name arg2 = RepositoryGenUtil.createVariableLiteral(ast, readerName);
		arguments.add(arg1);
		arguments.add(arg2);
		MethodInvocation methodInvocation = RepositoryGenUtil.createMethodInvocation(ast, ast.newSimpleName(adapterName), "readProperty", arguments);

		ReturnStatement returnStatement = ast.newReturnStatement();
		returnStatement.setExpression(methodInvocation);
		appendStatements.add(returnStatement);

		SwitchCase switchCase = getCaseLabel(caseLabelName);
		appendedSections.add(switchCase);
		appendedSections.addAll(appendStatements);
	}

	public RefAssociationInfo getRefAssociation(CefGeneratorContext cefGeneratorContext, IGspCommonField refElementField, GspAssociation gspAssociation){
		RefAssociationInfo refAssociationInfo = new RefAssociationInfo();

//		if(commonField.getChildAssociations() != null && commonField.getChildAssociations().size() > 0){
		GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
				getMetadata(gspAssociation.getRefModelID());
		GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
		IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
		for(IGspCommonField field: refObject.getContainElements()){
			if(field.getID().equals(refElementField.getRefElementId())){
				refAssociationInfo.setRefField(field);
				refAssociationInfo.setGspAssociation(gspAssociation);
				refAssociationInfo.setField(refElementField);
				return refAssociationInfo;
			}
		}
		IGspCommonField tempField = refObject.findElement(refElementField.getRefElementId());
		if(tempField != null){
			refAssociationInfo.setRefField(tempField);
			refAssociationInfo.setGspAssociation(tempField.getParentAssociation());
		}

		return refAssociationInfo;
	}

	private void buildEntityStatement(IGspCommonField gspCommonField, java.util.ArrayList<Statement> statements, java.util.ArrayList<Statement> appendedSections) {
		String propertyName = gspCommonField.getLabelID();
//		DbColumnInfo columnInfo = dbColumns.getItem(propertyName);


		if (gspCommonField.getChildAssociations().size() > 0) {
			buildAssociationStatement(gspCommonField, gspCommonField.getLabelID(), statements, appendedSections);
			return;
		}
		if (gspCommonField.getIsRefElement()) {
			return;
		}
		if (gspCommonField.getObjectType() == GspElementObjectType.Enum) {
			buildEnumStatement(gspCommonField, statements);
			return;
		}

		buildNormalPropertyReturnStatement(gspCommonField, statements);
	}


	private void buildDefaultStatement(java.util.ArrayList<Statement> statements) {
		SwitchCase caseStatement = ast.newSwitchCase();
		caseStatement.setExpression(null);
		statements.add(caseStatement);
		addImport(CefException.class.getTypeName());
		InfixExpression messageExpression = ast.newInfixExpression();
		messageExpression.setLeftOperand(RepositoryGenUtil.createStringLiteral(ast, "找不到属性名"));
		messageExpression.setRightOperand(RepositoryGenUtil.createVariableLiteral(ast, paramPropName));
		messageExpression.setOperator(InfixExpression.Operator.PLUS);
		ThrowStatement throwStatement = GeneratorUtil.getThrowStatement(ast,messageExpression);
		statements.add(throwStatement);
	}

	@Override
	protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
//		return new java.util.ArrayList<MethodAccessModifier>(java.util.Arrays.asList(new MethodAccessModifier[]{MethodAccessModifier.Public, MethodAccessModifier.Override}));
		ArrayList list = new ArrayList();
		list.add(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
		return list;
	}

	@Override
	protected boolean getIsOverride() {
		return true;
	}

	@Override
	protected String getMethodName() {
		return "readProperty";
	}

	@Override
	protected ArrayList<ParameterInfo> getParameterCollection() {
		ArrayList<ParameterInfo> list = new ArrayList<>();
		list.add(RepositoryGenUtil.createParameterInfo(String.class, paramPropName));
		list.add(RepositoryGenUtil.createParameterInfo(ICefReader.class, paramReader));
		return list;
	}


	@Override
	protected TypeInfo getReturnType() {
		return new TypeInfo(Object.class.getTypeName());
	}
}
